#pragma once
#include <iostream>
using namespace std;
#include "lockguard.hpp"
#include "Log.hpp"
#include "Thread.hpp"
#include <vector>
#include <queue>
#include <functional>
#include <unistd.h>
// #include "task.hpp"
#include <pthread.h>

#define default_size 5

class ThreadData
{
public:
    ThreadData(const std::string &name) : _threadname(name)
    {
    }
    ~ThreadData()
    {
    }

public:
    std::string _threadname;
};

template <class T>
class threadpool
{

private:
    threadpool(int size = default_size)
        : _size(size)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < size; i++)
        {
            string threadname = "thread-";
            threadname += to_string(i + 1);

            ThreadData td(threadname);
            _vt.emplace_back(threadname, bind(&threadpool::ThreadRun, this, placeholders::_1), td);
            log.LogMessage(Debug, "%s,Created\n", threadname.c_str());
        }
    }

    threadpool(threadpool<T>& cp) = delete;
    const threadpool<T>& operator=(threadpool<T>& cp) = delete;

public:
    static threadpool<T>* GetInsTance(int n = default_size)
    {
        if(ins == nullptr)
        {
            LockGuard lock(&initial_mutex);
            if(ins == nullptr)
            {
                ins = new threadpool<T>(n);
            }
        }
        return ins;
    }


    void Start()
    {
        for (auto &th : _vt)
        {
            th.Start();
            log.LogMessage(Debug, "%s,Has Been Start\n", th.GetThreadName().c_str());
        }
    }

    void ThreadRun(ThreadData &td)
    {
        while (true)
        {
            T t;
            {
                LockGuard lock(&_mutex);
                while (_queue.empty())
                {
                    pthread_cond_wait(&_cond, &_mutex);
                }

                t = _queue.front();
                _queue.pop();
            }
            log.LogMessage(Debug, "Create new Task ");
            t();
            log.LogMessage(Debug, "Finish Task ");
        }
    }

    void Push(const T &in)
    {
        LockGuard lock(&_mutex);
        _queue.push(in);
        pthread_cond_signal(&_cond);
    }

    // test
    void WaitJoin()
    {
        for (auto &th : _vt)
        {
            th.Join();
            log.LogMessage(Debug, "Has Been Join");
        }
    }

    ~threadpool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    queue<T> _queue;
    vector<Thread<ThreadData>> _vt;
    int _size;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static threadpool<T>* ins;
    static pthread_mutex_t initial_mutex;

};

template<class T>
threadpool<T>* threadpool<T>::ins = nullptr;
template<class T>
pthread_mutex_t threadpool<T>::initial_mutex = PTHREAD_MUTEX_INITIALIZER;